{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "To load \"alexandria\":\n",
      "  Load 1 ASDF system:\n",
      "    alexandria\n",
      "\n",
      "; Loading \"alexandria\"\n",
      "\n",
      "To load \"png\":\n",
      "  Load 1 ASDF system:\n",
      "    png\n",
      "\n",
      "; Loading \"png\"\n",
      "[package image]...................................\n",
      "[package png]; cc -o /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel-tmpAR3FSGEY.o -c -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -D_GNU_SOURCE -fno-omit-frame-pointer -DSBCL_HOME=/usr/lib/sbcl -g -Wall -Wundef -Wsign-compare -Wpointer-arith -O3 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wunused-parameter -Wimplicit-fallthrough -fno-omit-frame-pointer -momit-leaf-frame-pointer -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/usr/X11/include -I/opt/local/include -fPIC -I/home/yitzi/quicklisp/dists/quicklisp/software/cffi_0.23.0/ /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel.c\n",
      ".; cc -o /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel-tmpJAIDFZTC -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -g -Wl,--export-dynamic /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel.o\n",
      "; /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel.grovel-tmp.lisp\n",
      "; cc -o /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/wrappers__wrapper-tmp9V47YWQF.o -c -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -D_GNU_SOURCE -fno-omit-frame-pointer -DSBCL_HOME=/usr/lib/sbcl -g -Wall -Wundef -Wsign-compare -Wpointer-arith -O3 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wunused-parameter -Wimplicit-fallthrough -fno-omit-frame-pointer -momit-leaf-frame-pointer -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/usr/X11/include -I/opt/local/include -fPIC -I/home/yitzi/quicklisp/dists/quicklisp/software/cffi_0.23.0/ /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/wrappers__wrapper.c\n",
      "; cc -o /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/wrappers.so -shared -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -D_GNU_SOURCE -fno-omit-frame-pointer -DSBCL_HOME=/usr/lib/sbcl -g -Wall -Wundef -Wsign-compare -Wpointer-arith -O3 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wunused-parameter -Wimplicit-fallthrough -fno-omit-frame-pointer -momit-leaf-frame-pointer /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/wrappers__wrapper.o\n",
      "....................................\n",
      "[package bmp].....................................\n",
      "[package pnm]......\n",
      "To load \"iterate\":\n",
      "  Load 1 ASDF system:\n",
      "    iterate\n",
      "\n",
      "; Loading \"iterate\"\n",
      "\n",
      "To load \"flexi-streams\":\n",
      "  Load 1 ASDF system:\n",
      "    flexi-streams\n",
      "\n",
      "; Loading \"flexi-streams\"\n",
      "\n",
      "To load \"lparallel\":\n",
      "  Load 1 ASDF system:\n",
      "    lparallel\n",
      "\n",
      "; Loading \"lparallel\"\n",
      "[package lparallel.util]..........................\n",
      "[package lparallel.thread-util]...................\n",
      "[package lparallel.raw-queue].....................\n",
      "[package lparallel.cons-queue]....................\n",
      "[package lparallel.vector-queue]..................\n",
      "[package lparallel.queue].........................\n",
      "[package lparallel.counter].......................\n",
      "[package lparallel.spin-queue]....................\n",
      "[package lparallel.kernel]........................\n",
      "[package lparallel.kernel-util]...................\n",
      "[package lparallel.promise].......................\n",
      "[package lparallel.ptree].........................\n",
      "[package lparallel.slet]..........................\n",
      "[package lparallel.defpun]........................\n",
      "[package lparallel.cognate].......................\n",
      "[package lparallel]\n",
      "To load \"cl-cpus\":\n",
      "  Load 1 ASDF system:\n",
      "    cl-cpus\n",
      "\n",
      "; Loading \"cl-cpus\"\n",
      "[package cl-cpus]"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(:ALEXANDRIA :PNG :ITERATE :FLEXI-STREAMS :LPARALLEL :CL-CPUS)"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "(ql:quickload '(:alexandria :png :iterate :flexi-streams :lparallel :cl-cpus))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "#<LPARALLEL.KERNEL:KERNEL :NAME \"lparallel\" :WORKER-COUNT 16 :USE-CALLER NIL :ALIVE T :SPIN-COUNT 2000 {1004117613}>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(setf lparallel:*kernel* (lparallel:make-kernel (cpus:get-number-of-processors)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "JULIA-COUNT"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(defun julia-count (z c &key (max 255))\n",
    "  (do ((i 0 (1+ i))\n",
    "       (p z (+ (expt p 2) c)))\n",
    "      ((or (= i max)\n",
    "           (> (abs p) 2))\n",
    "        i)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "#<JUPYTER-WIDGETS:TRAIT-METACLASS COMMON-LISP-USER::JULIA-WIDGET>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "CALCULATE-ROW"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "UPDATE"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "#<STANDARD-METHOD COMMON-LISP:INITIALIZE-INSTANCE :AFTER (JULIA-WIDGET) {100480FA03}>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(defclass julia-widget (jupyter-widgets:grid-box)\n",
    "  ((image\n",
    "     :reader julia-image\n",
    "     :initform (make-instance 'jupyter-widgets:image\n",
    "                              :width 640 :height 640\n",
    "                              :layout (make-instance 'jupyter-widgets:layout :grid-area \"image\")))\n",
    "   (frame\n",
    "     :reader julia-frame\n",
    "     :initform (png:make-image 640 640 1 8))\n",
    "   (x\n",
    "     :reader julia-x\n",
    "     :initform (make-instance 'jupyter-widgets:float-text\n",
    "                              :value 0 :description \"x\"\n",
    "                              :layout (make-instance 'jupyter-widgets:layout :grid-area \"x\")))\n",
    "   (y\n",
    "     :reader julia-y\n",
    "     :initform (make-instance 'jupyter-widgets:float-text\n",
    "                              :value 0 :description \"y\"\n",
    "                              :layout (make-instance 'jupyter-widgets:layout :grid-area \"y\")))\n",
    "   (size\n",
    "     :reader julia-size\n",
    "     :initform (make-instance 'jupyter-widgets:float-text\n",
    "                              :value 4 :description \"size\"\n",
    "                              :layout (make-instance 'jupyter-widgets:layout :grid-area \"size\")))\n",
    "   (ca\n",
    "     :reader julia-ca\n",
    "     :initform (make-instance 'jupyter-widgets:float-text\n",
    "                              :value -0.8 :step 0.001 :description \"ca\"\n",
    "                              :layout (make-instance 'jupyter-widgets:layout :grid-area \"ca\")))\n",
    "   (cb\n",
    "     :reader julia-cb\n",
    "     :initform (make-instance 'jupyter-widgets:float-text\n",
    "                              :value 0.156 :step 0.001 :description \"cb\"\n",
    "                              :layout (make-instance 'jupyter-widgets:layout :grid-area \"cb\")))\n",
    "   (progress\n",
    "     :reader julia-progress\n",
    "     :initform (make-instance 'jupyter-widgets:int-progress\n",
    "                              :description \"Progress\"\n",
    "                              :max 640\n",
    "                              :layout (make-instance 'jupyter-widgets:layout :grid-area \"progress\")))\n",
    "   (task-channel\n",
    "     :reader julia-task-channel\n",
    "     :initform (lparallel:make-channel)))\n",
    "  (:metaclass jupyter-widgets:trait-metaclass)\n",
    "  (:default-initargs\n",
    "    :layout (make-instance 'jupyter-widgets:layout\n",
    "                           :grid-gap \".25em\"\n",
    "                           :grid-template-columns \"1fr min-content\"\n",
    "                           :grid-template-rows \"min-content min-content min-content min-content min-content min-content 1fr\"\n",
    "                           :grid-template-areas \"\\\"image x\\\" \\\"image y\\\" \\\"image size\\\" \\\"image ca\\\" \\\"image cb\\\" \\\"image progress\\\" \\\"image .\\\"\")))\n",
    "\n",
    "\n",
    "(defun calculate-row (frame y c width height xmin xmax ymin ymax)\n",
    "  (let ((zy (+ ymin (* (coerce (/ y height) 'float) (- ymax ymin)))))\n",
    "    (dotimes (x width)\n",
    "      (setf (aref frame y x 0)\n",
    "            (- 255 (julia-count (complex (+ xmin (* (coerce (/ x width) 'float) (- xmax xmin))) zy) c))))))\n",
    "\n",
    "\n",
    "(defun update (instance)\n",
    "  (bordeaux-threads:make-thread\n",
    "    (lambda ()\n",
    "      (with-slots (task-channel image frame x y size ca cb progress) instance\n",
    "        (let* ((c (complex (jupyter-widgets:widget-value ca)\n",
    "                           (jupyter-widgets:widget-value cb)))\n",
    "               (x-value (jupyter-widgets:widget-value x))\n",
    "               (y-value (jupyter-widgets:widget-value y))\n",
    "               (size-value (jupyter-widgets:widget-value size))\n",
    "               (width (jupyter-widgets:widget-width image))\n",
    "               (height (jupyter-widgets:widget-height image))\n",
    "               (xmin (- x-value (/ size-value 2)))\n",
    "               (xmax (+ x-value (/ size-value 2)))\n",
    "               (ymin (- y-value (/ size-value 2)))\n",
    "               (ymax (+ y-value (/ size-value 2))))\n",
    "          (dotimes (y height)\n",
    "            (lparallel:submit-task task-channel #'calculate-row frame y c width height xmin xmax ymin ymax))\n",
    "          (dotimes (y height)\n",
    "            (lparallel:receive-result task-channel)\n",
    "            (setf (jupyter-widgets:widget-value progress) y))\n",
    "          (setf (jupyter-widgets:widget-value image)\n",
    "                (flexi-streams:with-output-to-sequence (o)\n",
    "                                                       (png:encode frame o))))))))\n",
    "\n",
    "\n",
    "\n",
    "(defmethod initialize-instance :after ((instance julia-widget) &rest initargs &key &allow-other-keys)\n",
    "  (declare (ignore initargs))\n",
    "  (jupyter-widgets:observe (julia-x instance) :value\n",
    "                           (lambda (inst type name old-value new-value source)\n",
    "                             (declare (ignore inst type name old-value new-value source))\n",
    "                             (update instance)))\n",
    "  (jupyter-widgets:observe (julia-y instance) :value\n",
    "                           (lambda (inst type name old-value new-value source)\n",
    "                             (declare (ignore inst type name old-value new-value source))\n",
    "                             (update instance)))\n",
    "  (jupyter-widgets:observe (julia-size instance) :value\n",
    "                           (lambda (inst type name old-value new-value source)\n",
    "                             (declare (ignore inst type name old-value new-value source))\n",
    "                             (update instance)))\n",
    "  (jupyter-widgets:observe (julia-ca instance) :value\n",
    "                           (lambda (inst type name old-value new-value source)\n",
    "                             (declare (ignore inst type name old-value new-value source))\n",
    "                             (update instance)))\n",
    "  (jupyter-widgets:observe (julia-cb instance) :value\n",
    "                           (lambda (inst type name old-value new-value source)\n",
    "                             (declare (ignore inst type name old-value new-value source))\n",
    "                             (update instance)))\n",
    "  (setf (jupyter-widgets:widget-children instance)\n",
    "        (list (julia-image instance)\n",
    "              (julia-x instance)\n",
    "              (julia-y instance)\n",
    "              (julia-size instance)\n",
    "              (julia-ca instance)\n",
    "              (julia-cb instance)\n",
    "              (julia-progress instance)))\n",
    "  (update instance))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "d3f251c748f346f5379be2779be063b3",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "A Jupyter Widget"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "(make-instance 'julia-widget)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Common Lisp (SBCL)",
   "language": "common-lisp",
   "name": "common-lisp_sbcl"
  },
  "language_info": {
   "codemirror_mode": "text/x-common-lisp",
   "file_extension": ".lisp",
   "mimetype": "text/x-common-lisp",
   "name": "common-lisp",
   "pygments_lexer": "common-lisp",
   "version": "2.0.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
